-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Add message history API to agent run context for hook-driven conversation injections #2120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces a MessageHistory abstraction to the agent run context, enabling hooks to inspect and modify the conversation history visible to the model. This allows developers to inject additional messages, override turn inputs, and implement sophisticated patterns like safety layers, workflow guidance, and teaching flows.
Key changes:
- Added
MessageHistoryclass with methods to track, inject, and override conversation messages at specific lifecycle stages - Integrated message history into both
RunHooksandAgentHooksviaRunContextWrapper.message_history - Modified agent runner to apply injected messages and overrides while maintaining proper ordering around tool calls
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
src/agents/message_history.py |
New core abstraction for tracking and manipulating conversation history with stage-aware injection |
src/agents/run_context.py |
Added message_history field to RunContextWrapper for hook access |
src/agents/run.py |
Integrated message history lifecycle: binding inputs/items, consuming overrides, inserting injected items with proper ordering |
src/agents/_run_impl.py |
Wrapped tool hooks with injection stage markers and updated streaming to emit InjectedInputItem events |
src/agents/items.py |
Added InjectedInputItem type to represent hook-injected messages in run results |
src/agents/__init__.py |
Exported new InjectedInputItem and MessageHistory types |
tests/test_message_history.py |
Added comprehensive tests for message injection, streaming, and ordering guarantees |
docs/usage.md |
Documented message history API with examples and limitations |
docs/zh/usage.md |
Chinese documentation for message history feature |
docs/ko/usage.md |
Korean documentation for message history feature |
docs/ja/usage.md |
Japanese documentation for message history feature |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - [`Usage`][agents.usage.Usage] - 使用状況トラッキングのデータ構造 | ||
| - [`RequestUsage`][agents.usage.RequestUsage] - リクエストごとの使用状況の詳細 | ||
| - [`RunContextWrapper`][agents.run.RunContextWrapper] - 実行コンテキストから使用状況にアクセス | ||
| - [`MessageHistory`][agents.run_context.MessageHistory] - フックから会話履歴を閲覧・編集 |
Copilot
AI
Nov 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect module reference in documentation link. The link references [agents.run_context.MessageHistory] but based on the code structure, MessageHistory is defined in src/agents/message_history.py, so the reference should be [agents.message_history.MessageHistory].
| - [`MessageHistory`][agents.run_context.MessageHistory] - フックから会話履歴を閲覧・編集 | |
| - [`MessageHistory`][agents.message_history.MessageHistory] - フックから会話履歴を閲覧・編集 |
| ): | ||
| for item in new_step_items: | ||
| if isinstance(item, MessageOutputItem): | ||
| if isinstance(item, MessageOutputItem) or isinstance(item, InjectedInputItem): |
Copilot
AI
Nov 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code style: use isinstance(item, (MessageOutputItem, InjectedInputItem)) instead of isinstance(item, MessageOutputItem) or isinstance(item, InjectedInputItem) for better readability and performance.
| if isinstance(item, MessageOutputItem) or isinstance(item, InjectedInputItem): | |
| if isinstance(item, (MessageOutputItem, InjectedInputItem)): |
| return | ||
| if not self._stage_markers: | ||
| return | ||
| if self._stage_markers and self._stage_markers[-1] is marker: |
Copilot
AI
Nov 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant condition check. Line 176 checks if self._stage_markers after line 174 already checked if not self._stage_markers and returned early. The second check is unnecessary.
| if self._stage_markers and self._stage_markers[-1] is marker: | |
| if self._stage_markers[-1] is marker: |
| - [`Usage`][agents.usage.Usage] - Usage tracking data structure | ||
| - [`RequestUsage`][agents.usage.RequestUsage] - Per-request usage details | ||
| - [`RunContextWrapper`][agents.run.RunContextWrapper] - Access usage from run context | ||
| - [`MessageHistory`][agents.run_context.MessageHistory] - Inspect or edit the conversation from hooks |
Copilot
AI
Nov 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect module reference in documentation link. The link references [agents.run_context.MessageHistory] but based on the code structure, MessageHistory is defined in src/agents/message_history.py, so the reference should be [agents.message_history.MessageHistory].
| - [`MessageHistory`][agents.run_context.MessageHistory] - Inspect or edit the conversation from hooks | |
| - [`MessageHistory`][agents.message_history.MessageHistory] - Inspect or edit the conversation from hooks |
| - [`Usage`][agents.usage.Usage] - 用量跟踪数据结构 | ||
| - [`RequestUsage`][agents.usage.RequestUsage] - 按请求的用量详情 | ||
| - [`RunContextWrapper`][agents.run.RunContextWrapper] - 从运行上下文访问用量 | ||
| - [`MessageHistory`][agents.run_context.MessageHistory] - 在钩子中查看或编辑对话 |
Copilot
AI
Nov 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect module reference in documentation link. The link references [agents.run_context.MessageHistory] but based on the code structure, MessageHistory is defined in src/agents/message_history.py, so the reference should be [agents.message_history.MessageHistory].
| - [`MessageHistory`][agents.run_context.MessageHistory] - 在钩子中查看或编辑对话 | |
| - [`MessageHistory`][agents.message_history.MessageHistory] - 在钩子中查看或编辑对话 |
| - [`Usage`][agents.usage.Usage] - 사용량 추적 데이터 구조 | ||
| - [`RequestUsage`][agents.usage.RequestUsage] - 요청별 사용량 세부 정보 | ||
| - [`RunContextWrapper`][agents.run.RunContextWrapper] - 실행 컨텍스트에서 사용량 접근 | ||
| - [`MessageHistory`][agents.run_context.MessageHistory] - 훅에서 대화 기록을 조회/편집 |
Copilot
AI
Nov 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect module reference in documentation link. The link references [agents.run_context.MessageHistory] but based on the code structure, MessageHistory is defined in src/agents/message_history.py, so the reference should be [agents.message_history.MessageHistory].
| - [`MessageHistory`][agents.run_context.MessageHistory] - 훅에서 대화 기록을 조회/편집 | |
| - [`MessageHistory`][agents.message_history.MessageHistory] - 훅에서 대화 기록을 조회/편집 |
| return | ||
| try: | ||
| self._stage_markers.remove(marker) | ||
| except ValueError: |
Copilot
AI
Nov 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'except' clause does nothing but pass and there is no explanatory comment.
|
We've got similar suggestions in the past, but enabling this operation could make the whole agent app way more complicated and much harder to debug. For this reason, we still hesitate to add such a flexibility. |
Thank you for your response! I agree that it largely depends on the use case. In my situation, this is already the second project in a row where this feature becomes absolutely essential. Previously, I ran into several issues, for example: Technically, some of this could be solved by patching tool outputs, but in practice the model does not reliably follow such patches. More importantly, this kind of patching becomes an additional layer of complexity that actually makes debugging harder — exactly the concern you mentioned. I experimented with injecting developer messages through a hook, and in my case it significantly improved the agent’s stability and controllability. My use case involves long-running agents, so perhaps this feature is primarily valuable in scenarios like these. Nevertheless, I appreciate your review. I’ll be glad if at some point you decide this feature could be useful for your own cases as well. |
Summary
This PR introduces a
MessageHistoryabstraction on the agent run context so hooks can inspect and modify the effective conversation history seen by the model. Hooks can now inject additional messages, override the next turn’s input, and keep the transcript aligned with tool calls and streaming runs.Motivation
Hooks already let users observe the agent lifecycle, but they had no structured way to:
With
MessageHistory, you can implement patterns such as:Changes
MessageHistory(src/agents/message_history.py):RunItems and pending injected messages.get_messages()to snapshot the current transcript,add_message(...)to queue injected messages asInjectedInputItems,override_next_turn(...)to replace the next model call’s input,agent_start,before_llm,after_llm,before_tool,after_tool).RunContextWrapperwithmessage_history, available in bothRunHooksandAgentHooks.MessageHistoryintoAgentRunner:conversation_id/previous_response_id).InjectedInputItems before/after tool calls and around LLM calls.InjectedInputItemtoRunItemso injected messages show up innew_itemsand streaming events.Example
A custom set of hooks can now:
For instance, a
ShellToolexample usescontext.message_history.add_message(...)inon_start,on_llm_start,on_tool_startandon_tool_endto:result.to_input_list()alongside tool calls and outputs.Tests
tests/test_message_history.py:test_run_hooks_can_inject_messages_into_llm_inputtest_streamed_runs_emit_injected_input_itemstest_injected_messages_preserve_order_around_tool_callsAll tests pass with
make tests.Usage example:
now result contains: